
module axi_slave (

  aclk_i,
  areset_n_i,

  awid_i,
  awaddr_i,
  awlen_i,
  awsize_i,
  awburst_i,
  awvalid_i,
  awlock_i,
  awcache_i,
  awprot_i,
  awready_o,

  wid_i,
  wdata_i,
  wstrb_i,
  wlast_i,
  wvalid_i,
  wready_o,

  bid_o,
  bresp_o,
  bvalid_o,
  bready_i,

  arid_i,
  araddr_i,
  arlen_i,
  arsize_i,
  arburst_i,
  arvalid_i,
  arready_o,

  rid_o,
  rdata_o,
  rresp_o,
  rlast_o,
  rvalid_o,
  rready_i,

  reg_rd_addr_o,
  reg_rd_data_i,
  reg_rd_enab_o,
  reg_rd_datav_i,
  reg_rd_err_i,
  reg_wr_addr_o,
  reg_wr_data_o,
  reg_wr_datav_o,
  reg_wr_err_i,
  reg_wr_done_i
);


input           aclk_i;
input           areset_n_i;

input [3:0]     awid_i;
input [31:0]    awaddr_i;
input [3:0]     awlen_i;
input [2:0]     awsize_i;
input [1:0]     awburst_i;
input           awvalid_i;
input [1:0]     awlock_i;
input [3:0]     awcache_i;
input [2:0]     awprot_i;
output          awready_o;

input [3:0]     wid_i;
input [127:0]    wdata_i;
input [3:0]     wstrb_i;
input           wlast_i;
input           wvalid_i;
output          wready_o;

output [3:0]    bid_o;
output [1:0]    bresp_o;
output          bvalid_o;
input           bready_i;

input [3:0]     arid_i;
input [31:0]    araddr_i;
input [3:0]     arlen_i;
input [2:0]     arsize_i;
input [1:0]     arburst_i;
input           arvalid_i;
output          arready_o;
output [3:0]    rid_o;
output [127:0]   rdata_o;
output [1:0]    rresp_o;
output          rlast_o;
output          rvalid_o;
input           rready_i;

output [31:0]   reg_rd_addr_o;
input [127:0]   reg_rd_data_i;
output          reg_rd_enab_o;
input           reg_rd_err_i;
input           reg_rd_datav_i;

output [31:0]   reg_wr_addr_o;
output [127:0]  reg_wr_data_o;
output          reg_wr_datav_o;
input           reg_wr_err_i;
input           reg_wr_done_i;

reg             wready_o;
reg [3:0]       bid_o;
reg [1:0]       bresp_o;
reg             bvalid_o;
reg [127:0]      reg_wr_data_o;
reg             reg_wr_datav_o;
reg [3:0]       rid_o;
reg [127:0]      rdata_o;
reg [1:0]       rresp_o;
reg             rvalid_o;
reg             rlast_o;
reg             reg_rd_enab_o;


reg             wr_pend;
reg             d1_wr_pend;
reg             wr_access_err;
reg             wr_resp_pend;
reg             wr_size_err;
reg             d1_wlast;
reg             rd_resp_pend;
reg [31:0] awaddr_reg;
reg [3:0]  awid_reg;
reg rd_pend;
reg rd_size_err;
reg [31:0] araddr_reg;
reg [3:0]  arid_reg;
reg [3:0]  read_cnt, xfer_cnt;


  assign awready_o = !(wr_pend);


  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      d1_wr_pend <= 1'b0;
    else
      d1_wr_pend <= wr_pend;
  end
  
  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0) begin
      wr_pend <= 1'b0;
    end else if (wr_pend == 1'b0 && awvalid_i == 1'b1 )begin
      wr_pend <= 1'b1;
    end else if ((bready_i == 1'b1) && (bvalid_o == 1'b1) ) begin
      wr_pend <= 1'b0;
    end
  end

  always @ (posedge aclk_i or negedge areset_n_i)begin
    if (areset_n_i == 1'b0) begin
      wr_size_err <= 1'b0;
    end else if (wr_pend == 1'b0) begin
      if ((awvalid_i == 1'b1) && ((awsize_i != 3'h4) || (awburst_i != 2'b00) ||
                                  (awlen_i != 4'b0000))) begin
      							   
        wr_size_err <= 1'b1;
      end
    end else if (bvalid_o == 1'b1) begin
      wr_size_err <= 1'b0;
    end
  end
  
  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0) begin
      wr_access_err <= 1'b0;
    end else if (bvalid_o == 1'b1) begin
      wr_access_err <= 1'b0;
    end else if (wr_pend == 1'b1 && wvalid_i == 1'b1 && wready_o == 1'b1) begin
      if ((awid_reg != wid_i) || (wstrb_i != 4'hF)) begin
        wr_access_err <= 1'b1;
      end
    end
  end
  
  assign reg_wr_addr_o = awaddr_reg;
  
  always @(posedge aclk_i or negedge areset_n_i)
  begin
    if (areset_n_i == 1'b0) begin
      awaddr_reg <= 32'b0;
      awid_reg <= 4'b0;
    end else if ((wr_pend == 1'b0) && (awvalid_i == 1'b1))begin
      awaddr_reg <= awaddr_i;
      awid_reg <= awid_i;
    end
  end
  
  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      wready_o <= 1'b0;
	  
    else if (wr_pend == 1'b0 && awvalid_i == 1'b1 )
      wready_o <= 1'b1;
    else if (wlast_i == 1'b1)
      wready_o <= 1'b0;
  end

  
  always @ (posedge aclk_i or negedge areset_n_i)
  begin
    if (areset_n_i == 1'b0) begin
      reg_wr_data_o  <= 128'h0;
      reg_wr_datav_o <= 1'b0;
    end else if ((wvalid_i == 1'b1)&& (wready_o == 1'b1)) begin
      reg_wr_data_o   <= wdata_i;
      if ((awid_reg == wid_i) && (wstrb_i == 4'hF))
	  
        reg_wr_datav_o <= ~wr_size_err ;
      else
        reg_wr_datav_o <= 1'd0;
    end else begin
      reg_wr_datav_o <= 1'b0;
      reg_wr_data_o   <= 128'h0;
    end
  end
  

  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0) begin
      bid_o <= 4'd0;
      d1_wlast <= 1'd0;
    end else begin
      bid_o <= awid_reg;
      d1_wlast <= wlast_i;
    end
  end
  
  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0) begin
      bresp_o <= 2'h0;
      bvalid_o <= 1'b0;
    end else if ((wr_resp_pend == 1'b0) &&
                 ((reg_wr_done_i == 1'b1) ||
                  (wr_access_err == 1'b1 && d1_wlast == 1'b1) ||
                  (wr_size_err == 1'b1 && wlast_i == 1'b1))) begin
      bvalid_o <= 1'b1;
      if ((reg_wr_err_i == 1'b1) || (wr_access_err == 1'b1) ||
          (wr_size_err == 1'b1))
        bresp_o <= 2'b10;
      else
        bresp_o <= 2'b00;
    end else if (wr_resp_pend == 1'b0) begin
      bresp_o <= 2'b00;
      bvalid_o <= 1'b0;
    end else if (bready_i == 1'b1) begin
      bresp_o <= 2'b00;
      bvalid_o <= 1'b0;
    end
  end
  
  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      wr_resp_pend <= 1'b0;
    else if ((wr_resp_pend == 1'b0) &&
             ((reg_wr_done_i == 1'b1) ||
              (wr_access_err == 1'b1 && d1_wlast == 1'b1) ||
              (wr_size_err == 1'b1 && wlast_i == 1'b1)))
      wr_resp_pend <= ~bready_i;
    else if (bready_i == 1'b1)
      wr_resp_pend <= 1'b0;
  end


  
  assign arready_o = !rd_pend;

  
  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0) begin
      rd_pend <= 1'b0;
    end else if (rd_pend == 1'b0 && arvalid_i == 1'b1 )begin
      rd_pend <= 1'b1;
    end else if ((rready_i == 1'b1) && (rlast_o == 1'b1)) begin
      rd_pend <= 1'b0;
    end
  end

  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0) begin
      rd_size_err <= 1'b0;
    end else if (rd_pend == 1'b0) begin
      if ((arvalid_i == 1'b1) && ((arsize_i != 3'h4) || (arburst_i != 2'b00))) begin
        rd_size_err <= 1'b1;
      end
    end else if (rlast_o == 1'b1) begin
      rd_size_err <= 1'b0;
    end
  end
  
  always @(posedge aclk_i or negedge areset_n_i)
  begin
    if (areset_n_i == 1'b0) begin
      araddr_reg <= 32'b0;
      arid_reg <= 4'b0;
    end else if ((rd_pend == 1'b0) && (arvalid_i == 1'b1))begin
      araddr_reg <= araddr_i;
      arid_reg <= arid_i;
    end
  end
  
  always @(posedge aclk_i or negedge areset_n_i)
  begin
    if (areset_n_i == 1'b0) begin
      xfer_cnt <= 4'b0;
    end else if ((rd_pend == 1'b0) && (arvalid_i == 1'b1))begin
      xfer_cnt <= arlen_i;
	  
    end else if (xfer_cnt != 4'd0 && rready_i == 1'b1 && rvalid_o == 1'b1)
      xfer_cnt <= xfer_cnt - 4'd1;
	  
 end
 
  assign reg_rd_addr_o = araddr_reg;
  always @(posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0) begin
      reg_rd_enab_o <= 1'b0;
    end else if (rd_pend == 1'b0) begin
      if ((arvalid_i == 1'b1) && ((arsize_i == 3'h4) || (arburst_i == 2'b00)))
        reg_rd_enab_o <= 1'b1;
    end else if (rd_resp_pend == 1'b1 && rready_i == 1'b1) begin
	
      if (xfer_cnt != 4'd0)
        reg_rd_enab_o <= ~rd_size_err;
		
    end else begin
      reg_rd_enab_o <= 1'b0;
    end
  end
  
  always @(posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0) begin
      rid_o <= 4'h0;
      rdata_o <= 128'h0;
      rresp_o <= 2'h0;
      rvalid_o <= 1'b0;
      rlast_o <= 1'b0;
    end else if (rd_pend == 1'b1)
      if (rd_resp_pend == 1'b1) begin
        if (rready_i == 1'b1) begin
          rvalid_o <= 1'b0;
          rlast_o  <= 1'b0;
        end
      end else if ((reg_rd_datav_i == 1'b1) || (rd_size_err == 1'b1)) begin  
        rvalid_o <= 1'b1;
        rid_o <= arid_reg;
        rlast_o <= (xfer_cnt == 4'd0)? 1'b1: 1'b0;
        rdata_o <= reg_rd_data_i;
        if ((reg_rd_err_i == 1'b1) || (rd_size_err == 1'b1))
          rresp_o <= 2'b10;
        else
          rresp_o <= 2'b00;
      end
    else begin   
      rid_o <= 4'h0;
      rdata_o <= 128'h0;
      rresp_o <= 2'h0;
      rvalid_o <= 1'b0;
      rlast_o <= 1'b0;
    end
  end
  
  always @(posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      rd_resp_pend <= 1'b0;
    else if (rd_pend == 1'b1) begin
      if (rd_resp_pend == 1'b1)
        rd_resp_pend <= ~rready_i;
      else if ((reg_rd_datav_i == 1'b1) || (rd_size_err == 1'b1))   
        rd_resp_pend <= 1'b1;
    end else
      rd_resp_pend <= 1'b0;
  end

endmodule
